//! 一个简单的P2P聊天应用
//! 该应用使用libp2p库实现了一个简单的P2P聊天功能，支持发送和接收消息。
//! 它使用TCP传输和Yamux多路复用，以及Noise加密协议来保证安全通信。
//! 应用通过监听一个端口并与其他节点建立连接，然后通过JSON协议进行消息交换。
//! 该应用可以作为一个基础示例，用于学习和理解P2P网络编程。
//!
//! 环境变量：
//! - CHAT_P2P_PORT: 监听端口，默认为9000
//! - CHAT_P2P_PEER: 要连接的peer地址
//!
//! 代码设计解释：
//!
//! 网络架构：
//! 使用libp2p的Swarm管理网络连接
//! 采用TCP传输层，Noise加密和Yamux多路复用
//! 使用请求-响应模式进行消息传递
//!
//! 核心功能：
//! Ping协议：每10秒发送一次ping保持连接活跃
//! 消息协议：自定义"/awesome-chat/1"协议，使用JSON序列化
//! 双工通信：支持发送消息和接收确认
//!
//! 事件处理：
//! 使用tokio::select!同时处理网络事件和用户输入
//! 详细处理各种网络事件（连接建立/关闭、消息收发等）
//!
//! 错误处理：
//! 使用anyhow简化错误处理
//! 记录各种失败情况（发送失败、接收失败等）
//!
//! 用户交互：
//! 从标准输入读取消息
//! 自动将第一个连接的peer设为目标
//! 显示本地和远程peer的消息
//! 这个实现展示了P2P网络的基本原理，包括节点发现、连接管理、可靠消息传递等核心功能，可以作为更复杂P2P应用的基础。
//!
//! 运行示例
//!
//! 开一个终端，运行以下命令：
//! ```bash
//! make examples/p2p_chat1
//! ```
//! 再开一个终端，运行以下命令：
//! ```bash
//! make examples/p2p_chat2
//! ```
//! 可以看到两个终端之间可以相互发送消息。
//!
use std::time::Duration;

// 引入anyhow的错误处理库，用于简化错误处理
#[allow(unused)]
use anyhow::Context;

// 引入libp2p相关模块
use libp2p::{
    Multiaddr,
    StreamProtocol,
    SwarmBuilder,       // 多地址、流协议和Swarm构建器
    futures::StreamExt, // 异步流扩展
    noise,
    ping,                                  // 加密和ping协议
    request_response::{self, json},        // 请求-响应模式和JSON序列化
    swarm::{NetworkBehaviour, SwarmEvent}, // 网络行为和Swarm事件
    tcp,
    yamux, // TCP传输和Yamux多路复用
};
use serde::{Deserialize, Serialize}; // 序列化/反序列化
use tokio::io::{self, AsyncBufReadExt, BufReader}; // 异步IO

// 定义消息请求结构体，用于发送聊天消息
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MessageRequest {
    pub message: String, // 消息内容
}

// 定义消息响应结构体，用于确认消息接收
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MessageResponse {
    pub ack: bool, // 确认标志
}

// 定义网络行为组合，包含ping和消息功能
#[derive(NetworkBehaviour)]
struct ChatBehavior {
    ping: ping::Behaviour, // 用于保持连接活跃的ping行为
    messaging: json::Behaviour<MessageRequest, MessageResponse>, // 基于JSON的消息行为
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // 从环境变量获取监听端口，默认9000
    let port: u16 = std::env::var("CHAT_P2P_PORT")
        .unwrap_or("9000".to_string())
        .parse()?;

    // 从环境变量获取要连接的peer地址
    let peer: Multiaddr = std::env::var("CHAT_P2P_PEER")?.parse()?;

    // 构建Swarm实例
    let mut swarm = SwarmBuilder::with_new_identity() // 创建新身份
        .with_tokio() // 使用tokio运行时
        .with_tcp(
            // 使用TCP传输
            tcp::Config::default(),
            noise::Config::new,     // 使用Noise加密
            yamux::Config::default, // 使用Yamux多路复用
        )?
        .with_behaviour(|_key_pair| {
            // 配置网络行为
            Ok(ChatBehavior {
                ping: ping::Behaviour::new(
                    // 每10秒发送一次ping
                    ping::Config::new().with_interval(Duration::from_secs(10)),
                ),
                messaging: json::Behaviour::new(
                    // 配置消息协议
                    [(
                        StreamProtocol::new("/awesome-chat/1"),  // 协议ID
                        request_response::ProtocolSupport::Full, // 支持请求和响应
                    )],
                    request_response::Config::default(),
                ),
            })
        })?
        .with_swarm_config(|config| config.with_idle_connection_timeout(Duration::from_secs(30))) // 30秒空闲超时
        .build();

    // 开始监听指定端口
    swarm.listen_on(format!("/ip4/0.0.0.0/tcp/{port}").parse()?)?;
    // 连接到指定的peer
    swarm.dial(peer.clone())?;

    println!("Peer ID: {:?}", swarm.local_peer_id());

    // 准备从标准输入读取消息
    let mut stdin = BufReader::new(io::stdin()).lines();

    let mut target_peer_id = None; // 存储目标peer ID
    loop {
        tokio::select! {  // 同时处理网络事件和用户输入
            event = swarm.select_next_some() => match event {
                SwarmEvent::NewListenAddr { address, .. } => {
                    println!("Listening on {:?}", address);
                }
                SwarmEvent::ConnectionEstablished { peer_id, .. } => {
                    println!("Established connection to peer: {:?}", peer_id);
                    if target_peer_id.is_none() {  // 如果是第一个连接，设为目标peer
                        target_peer_id = Some(peer_id);
                    }
                    swarm.add_peer_address(peer_id, peer.clone());  // 添加peer地址
                },
                SwarmEvent::ConnectionClosed { peer_id,.. } => {
                    println!("Connection closed to peer: {:?}", peer_id);
                    target_peer_id = None; // 连接关闭，清空目标peer ID
                }
                SwarmEvent::Behaviour(event) => match event {
                    ChatBehaviorEvent::Ping(event) => {
                        println!("Received ping: {:?}", event);
                    }
                    ChatBehaviorEvent::Messaging(event) => match event {
                        request_response::Event::Message {
                            peer, connection_id, message
                        } => match message {
                            // 处理接收到的消息请求
                            request_response::Message::Request { request_id, request, channel } => {
                                println!("Received request from peer: {:?}, connection_id: {:?}, request_id: {:?}, request: {:?}", peer, connection_id, request_id, request);
                                let response = MessageResponse { ack: true };
                                // 发送确认响应
                                if let Err(e) = swarm
                                    .behaviour_mut()
                                    .messaging
                                    .send_response(channel, response) {
                                        println!("Error sending response: {:?}", e);
                                    }
                            },
                            // 处理接收到的消息响应
                            request_response::Message::Response { request_id, response,.. } => {
                                println!("Received response from peer: {:?}, connection_id: {:?}, request_id: {:?}, response: {:?}", peer, connection_id, request_id, response);
                            }
                        },
                        // 响应发送成功事件
                        request_response::Event::ResponseSent { .. } => { /* ... */ },
                        // 出站失败事件
                        request_response::Event::OutboundFailure { .. } => { /* ... */ },
                        // 入站失败事件
                        request_response::Event::InboundFailure { .. } => { /* ... */ },
                    }
                }
                _ => {}
            },

            // 处理用户输入
            Ok(Some(line)) = stdin.next_line() => {
                if let Some(target_peer_id) = target_peer_id {
                    let message = MessageRequest { message: line.clone() };
                    // 发送消息到目标peer
                    let _ = swarm
                        .behaviour_mut()
                        .messaging
                        .send_request(&target_peer_id, message);
                    println!("{}: {:?}", swarm.local_peer_id(), line);
                }
            }
        }
    }
}
